// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// package strconv -- go2cs converted at 2022 March 13 05:41:24 UTC
// import "strconv" ==> using strconv = go.strconv_package
// Original source: C:\Program Files\Go\src\strconv\itoa.go
namespace go;

using bits = math.bits_package;

public static partial class strconv_package {

private static readonly var fastSmalls = true; // enable fast path for small integers

// FormatUint returns the string representation of i in the given base,
// for 2 <= base <= 36. The result uses the lower-case letters 'a' to 'z'
// for digit values >= 10.
 // enable fast path for small integers

// FormatUint returns the string representation of i in the given base,
// for 2 <= base <= 36. The result uses the lower-case letters 'a' to 'z'
// for digit values >= 10.
public static @string FormatUint(ulong i, nint @base) {
    if (fastSmalls && i < nSmalls && base == 10) {
        return small(int(i));
    }
    var (_, s) = formatBits(null, i, base, false, false);
    return s;
}

// FormatInt returns the string representation of i in the given base,
// for 2 <= base <= 36. The result uses the lower-case letters 'a' to 'z'
// for digit values >= 10.
public static @string FormatInt(long i, nint @base) {
    if (fastSmalls && 0 <= i && i < nSmalls && base == 10) {
        return small(int(i));
    }
    var (_, s) = formatBits(null, uint64(i), base, i < 0, false);
    return s;
}

// Itoa is equivalent to FormatInt(int64(i), 10).
public static @string Itoa(nint i) {
    return FormatInt(int64(i), 10);
}

// AppendInt appends the string form of the integer i,
// as generated by FormatInt, to dst and returns the extended buffer.
public static slice<byte> AppendInt(slice<byte> dst, long i, nint @base) {
    if (fastSmalls && 0 <= i && i < nSmalls && base == 10) {
        return append(dst, small(int(i)));
    }
    dst, _ = formatBits(dst, uint64(i), base, i < 0, true);
    return dst;
}

// AppendUint appends the string form of the unsigned integer i,
// as generated by FormatUint, to dst and returns the extended buffer.
public static slice<byte> AppendUint(slice<byte> dst, ulong i, nint @base) {
    if (fastSmalls && i < nSmalls && base == 10) {
        return append(dst, small(int(i)));
    }
    dst, _ = formatBits(dst, i, base, false, true);
    return dst;
}

// small returns the string for an i with 0 <= i < nSmalls.
private static @string small(nint i) {
    if (i < 10) {
        return digits[(int)i..(int)i + 1];
    }
    return smallsString[(int)i * 2..(int)i * 2 + 2];
}

private static readonly nint nSmalls = 100;



private static readonly @string smallsString = "00010203040506070809" + "10111213141516171819" + "20212223242526272829" + "30313233343536373839" + "40414243444546474849" + "50515253545556575859" + "60616263646566676869" + "70717273747576777879" + "80818283848586878889" + "90919293949596979899";



private static readonly var host32bit = ~uint(0) >> 32 == 0;



private static readonly @string digits = "0123456789abcdefghijklmnopqrstuvwxyz";

// formatBits computes the string representation of u in the given base.
// If neg is set, u is treated as negative int64 value. If append_ is
// set, the string is appended to dst and the resulting byte slice is
// returned as the first result value; otherwise the string is returned
// as the second result value.
//


// formatBits computes the string representation of u in the given base.
// If neg is set, u is treated as negative int64 value. If append_ is
// set, the string is appended to dst and the resulting byte slice is
// returned as the first result value; otherwise the string is returned
// as the second result value.
//
private static (slice<byte>, @string) formatBits(slice<byte> dst, ulong u, nint @base, bool neg, bool append_) => func((_, panic, _) => {
    slice<byte> d = default;
    @string s = default;

    if (base < 2 || base > len(digits)) {
        panic("strconv: illegal AppendInt/FormatInt base");
    }
    array<byte> a = new array<byte>(64 + 1); // +1 for sign of 64bit value in base 2
    var i = len(a);

    if (neg) {
        u = -u;
    }
    if (base == 10) { 
        // common case: use constants for / because
        // the compiler can optimize it into a multiply+shift

        if (host32bit) { 
            // convert the lower digits using 32bit operations
            while (u >= 1e9F) { 
                // Avoid using r = a%b in addition to q = a/b
                // since 64bit division and modulo operations
                // are calculated by runtime functions on 32bit machines.
                var q = u / 1e9F;
                var us = uint(u - q * 1e9F); // u % 1e9 fits into a uint
                for (nint j = 4; j > 0; j--) {
                    var @is = us % 100 * 2;
                    us /= 100;
                    i -= 2;
                    a[i + 1] = smallsString[is + 1];
                    a[i + 0] = smallsString[is + 0];
                } 

                // us < 10, since it contains the last digit
                // from the initial 9-digit us.
 

                // us < 10, since it contains the last digit
                // from the initial 9-digit us.
                i--;
                a[i] = smallsString[us * 2 + 1];

                u = q;
            } 
            // u < 1e9
 
            // u < 1e9
        }
        us = uint(u);
        while (us >= 100) {
            @is = us % 100 * 2;
            us /= 100;
            i -= 2;
            a[i + 1] = smallsString[is + 1];
            a[i + 0] = smallsString[is + 0];
        } 

        // us < 100
        @is = us * 2;
        i--;
        a[i] = smallsString[is + 1];
        if (us >= 10) {
            i--;
            a[i] = smallsString[is];
        }
    }
    else if (isPowerOfTwo(base)) { 
        // Use shifts and masks instead of / and %.
        // Base is a power of 2 and 2 <= base <= len(digits) where len(digits) is 36.
        // The largest power of 2 below or equal to 36 is 32, which is 1 << 5;
        // i.e., the largest possible shift count is 5. By &-ind that value with
        // the constant 7 we tell the compiler that the shift count is always
        // less than 8 which is smaller than any register width. This allows
        // the compiler to generate better code for the shift operation.
        var shift = uint(bits.TrailingZeros(uint(base))) & 7;
        var b = uint64(base);
        var m = uint(base) - 1; // == 1<<shift - 1
        while (u >= b) {
            i--;
            a[i] = digits[uint(u) & m];
            u>>=shift;
        }
    else
 
        // u < base
        i--;
        a[i] = digits[uint(u)];
    } { 
        // general case
        b = uint64(base);
        while (u >= b) {
            i--; 
            // Avoid using r = a%b in addition to q = a/b
            // since 64bit division and modulo operations
            // are calculated by runtime functions on 32bit machines.
            q = u / b;
            a[i] = digits[uint(u - q * b)];
            u = q;
        } 
        // u < base
        i--;
        a[i] = digits[uint(u)];
    }
    if (neg) {
        i--;
        a[i] = '-';
    }
    if (append_) {
        d = append(dst, a[(int)i..]);
        return ;
    }
    s = string(a[(int)i..]);
    return ;
});

private static bool isPowerOfTwo(nint x) {
    return x & (x - 1) == 0;
}

} // end strconv_package
